/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <paper/paper.h>
#include <corelib/corelib.h>

// VM handler definition
class vm_test1_handler : public paper::vmachine_hnd
{

public:
	// dynamic error report
    void dynError( const paper::vm_error &error )
	{
		std::cout << "error: " << error.error() << std::endl;
	}

	// 
    void open( const corelib::ustring &path )
	{

	}

	// 
    void compile( const corelib::ustring &path )
	{

	}

	// 
    corelib::uint8 getc( void )
	{

	}

	// 
    void close()
	{

	}

};

class test{

public:
	//
	int main();
	// 
	int load_class_method();
	//
	int load_code_miscellanea();

protected:
	// VM & handler
	paper::vmachine vm;
	vm_test1_handler hnd;
	// classes and methods
	paper::vm_class* _class;
	paper::method* _method3;

};

int test::main(){
	// set handler
	vm.set_hnd(hnd);

	// load all libraries
	load_all_libs( vm );

	int ret = 0;
	// load classes and methods
	if(ret = load_class_method() | load_code_miscellanea()){
		return ret;
	}

	reginteger( vm.argument(1) ) = 24;

	// call function
	if(!vm.run(_class->name(), _method3->meta_name())){
		return -1;
	}

	std::cout << "fibonacci(24): " << reginteger( vm.lret() ) << std::endl;
	return 0;
}

int test::load_class_method(){
	/** 
	 * add class
	 */
	corelib::pair<paper::vm_class*, bool> testc = vm.add_class( "test.Test" );
	// check addition
	if(!testc.second){ return -1; }
	// get class
	_class = testc.first;

	/** 
	 * add methods
	 */	

	corelib::pair<paper::method*, bool> testm3 = _class->add_method( "fibonacci", "mthd_1_", 1 );
	// check addition
	if(!testm3.second){ return -1; }
	// get class
	_method3 = testm3.first;

	//
	return 0;
}

int test::load_code_miscellanea(){
	/* 
	 * Fibonacci
	 */

	// allocate memory
	if(	!_method3->set_stack_frame_size( 0, 4 ) ){
		std::cout << "error allocating method3 memory" << std::endl;
		return -1;
	}

	// set method static
	_method3->set_flags( paper::FLG_MTHD_STATIC );
	_method3->add_inst_b(paper::OP_LOADIZ,11,2); // R11 = 2

	// if(a<2) return a;
	_method3->add_inst_a(paper::OP_GEI,1,11,0);
	_method3->add_inst_b(paper::OP_RCJMP,0,2);
	_method3->add_inst_b(paper::OP_RETURN,1,0);

	// return fibonacci(a-1) + fibonacci(a-2);
	_method3->add_inst_b(paper::OP_DECI,0,REG_TOOGLE(1));
	_method3->add_inst_a(paper::OP_LOADARG,1,1,0);
	_method3->add_inst_b(paper::OP_KCCALL, 
		_method3->idx(),_method3->pclass()->idx());
	_method3->add_inst_a(paper::OP_MOVE,12,10,0);
	_method3->add_inst_b(paper::OP_DECI,0,REG_TOOGLE(1));
	_method3->add_inst_a(paper::OP_LOADARG,1,1,0);
	_method3->add_inst_b(paper::OP_KCCALL, 
		_method3->idx(),_method3->pclass()->idx());
	_method3->add_inst_a(paper::OP_ADDI,10,10,12);
	_method3->add_inst_b(paper::OP_RETURN,10,0);

	return( 0 );
}

int main(char *argv[], int argc){
	test test1;
	return( test1.main() );
}

